package com.gitee.wsl.io


import com.gitee.wsl.api.closeQuietly
import kotlinx.io.IOException
import kotlinx.io.Sink
import kotlinx.io.Source
import kotlinx.io.files.FileNotFoundException
import kotlinx.io.files.FileSystem
import kotlinx.io.files.Path
import kotlinx.io.files.SystemTemporaryDirectory
import kotlin.random.Random
import kotlin.random.nextULong


/**
 * Creates a sink to write [file], executes [writerAction] to write it, and then closes the sink.
 * This is a compact way to write a file.
 *
 * @param mustCreate true to throw an [IOException] instead of overwriting an existing file.
 *     This is equivalent to `O_EXCL` on POSIX and `CREATE_NEW` on Windows.
 */
@Throws(IOException::class)
fun FileSystem.write(
    file: Path,
    mustCreate: Boolean = false,
    append: Boolean = false,
    writerAction: Sink.() -> Unit,
){
    File(file, fileSystem = this).writeBuffered(append = append,block = writerAction)
}

@Throws(IOException::class)
inline fun <T> FileSystem.read(file: Path,
                               mustCreate: Boolean = false,
                               readAction: Source.() -> T,
): T{
    return File(file, fileSystem = this).readSource().readAction()
}

//fun FileSystem.createFile(file: Path, mustCreate:Boolean = false){
//    if(file.exists() && mustCreate) file.delete()
//    file.toFile().createIfNoExists()
//}

fun FileSystem.createFile(file: Path, mustCreate: Boolean = false) {
    file.parent?.createDirectoriesIfNoExists(this)
    if (mustCreate) {
        sink(file).closeQuietly()
    } else if (!exists(file)) {
        sink(file).closeQuietly()
    }
}

fun FileSystem.atomicMove(source: Path, target: Path, deleteTarget: Boolean) {
    if (deleteTarget) delete(target)
    atomicMove(source, target)
}

fun FileSystem.createTempFile(): Path {
    var tempFile: Path
    do {
        tempFile = SystemTemporaryDirectory / "tmp_${Random.nextULong()}"
    } while (exists(tempFile))
    createFile(tempFile, mustCreate = true)
    return tempFile
}

/**
 * Tolerant delete, try to clear as many files as possible even after a failure.
 *
 */
fun FileSystem.deleteContents(directory: Path) {
    var exception: IOException? = null
    val files = try {
        list(directory)
    } catch (_: FileNotFoundException) {
        return
    }
    for (file in files) {
        try {
            if (file.isDirectory(this)) {
                deleteContents(file)
            }
            delete(file)
        } catch (e: IOException) {
            if (exception == null) {
                exception = e
            }
        }
    }
    if (exception != null) {
        throw exception
    }
}